package com.custom.action.core.methods;

import com.custom.action.condition.AbstractUpdateSet;
import com.custom.action.condition.ConditionWrapper;
import com.custom.action.core.HandleSelectSqlBuilder;
import com.custom.action.core.TableInfoCache;
import com.custom.action.core.syncquery.SyncQueryWrapper;
import com.custom.action.core.syncquery.SyncResultHandler;
import com.custom.action.dbaction.AbstractSqlBuilder;
import com.custom.action.interfaces.ExecuteHandler;
import com.custom.action.joiner.JoinConditionWrapper;
import com.custom.action.model.ActiveBaseModel;
import com.custom.action.model.BaseModelHandler;
import com.custom.comm.enums.SqlExecTemplate;
import com.custom.comm.page.DbPageRows;
import com.custom.comm.utils.AssertUtil;
import com.custom.comm.utils.Converts;
import com.custom.jdbc.executebody.BaseSqlExecutorBody;
import com.custom.jdbc.executebody.ExecuteBodyHelper;
import com.custom.jdbc.interfaces.CustomSqlSession;
import com.custom.jdbc.interfaces.DatabaseAdapter;
import com.custom.jdbc.session.JdbcSqlSessionFactory;

import java.util.*;

/**
 * @author Xiao-Bai
 * @since 2023/3/8 19:02
 */
@SuppressWarnings("unchecked")
public abstract class AbstractMethod implements ExecuteHandler {

    protected static boolean sqlPrintSupport = true;

    protected abstract <T> CustomSqlSession createSqlSession(JdbcSqlSessionFactory sqlSessionFactory,
                                                             Class<T> target,
                                                             Object[] params) throws Exception;

    protected abstract <T> Object doExecute(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target, Object[] params) throws Exception;


    public <T> Class<T> getMappedType(Object[] params) {
        return getMappedType(params, 0);
    }


    @Override
    public <T> Object startExecute(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target, Object[] params) throws Exception {
        Object result = doExecute(sqlSessionFactory, target, params);
        resultPostProcess(result);
        return result;
    }

    private static void resultPostProcess(Object result) {
        if (result == null) return;
        if (result instanceof List || result instanceof Set) {
            handleAfter((Collection<?>) result);
        }else if (result instanceof ActiveBaseModel) {
            handleAfter(Collections.singletonList(result));
        }else if (result instanceof DbPageRows) {
            handleAfter(((DbPageRows<?>) result).getData());
        }
    }

    private static void handleAfter(Collection<?> result) {
        if (result.isEmpty()) return;
        if (!(result.iterator().next() instanceof ActiveBaseModel)) return;
        List<?> list = new ArrayList<>(result);
        list.forEach(it -> ((BaseModelHandler) it).queryAfter());
    }

    protected <T> Class<T> getMappedType(Object[] params, int index) {
        AssertUtil.checkTrue(params.length > 0 && params[index] != null, "MappedType cannot be null");
        Object param = params[index];
        if (param instanceof CharSequence) {
            return (Class<T>) String.class;
        }
        if (param instanceof ConditionWrapper) {
            ConditionWrapper<T> conditionWrapper = (ConditionWrapper<T>) param;
            return conditionWrapper.getEntityClass();
        }
        if (param instanceof JoinConditionWrapper) {
            JoinConditionWrapper<T> joinConditionWrapper = (JoinConditionWrapper<T>) param;
            return joinConditionWrapper.getEntityClass();
        }
        if (param instanceof SyncQueryWrapper) {
            SyncQueryWrapper<T> queryWrapper = (SyncQueryWrapper<T>) params[0];
            return queryWrapper.getEntityClass();
        }
        if (param instanceof AbstractUpdateSet) {
            AbstractUpdateSet<T> updateSet = (AbstractUpdateSet<T>) param;
            return updateSet.thisEntityClass();
        }
        return param instanceof Class ?
                (Class<T>) param : (Class<T>) param.getClass();
    }


    protected <T> AbstractSqlBuilder<T> getSelectSqlBuilder(JdbcSqlSessionFactory sqlSessionFactory, Class<T> mappedType) {
        return TableInfoCache.getSelectSqlBuilderCache(mappedType, sqlSessionFactory);
    }

    protected <T> AbstractSqlBuilder<T> getInsertSqlBuilder(JdbcSqlSessionFactory sqlSessionFactory, Class<T> mappedType) {
        return TableInfoCache.getInsertSqlBuilderCache(mappedType, sqlSessionFactory);
    }

    protected <T> AbstractSqlBuilder<T> getDeleteSqlBuilder(JdbcSqlSessionFactory sqlSessionFactory, Class<T> mappedType) {
        return TableInfoCache.getDeleteSqlBuilderCache(mappedType, sqlSessionFactory);
    }

    protected <T> AbstractSqlBuilder<T> getUpdateSqlBuilder(JdbcSqlSessionFactory sqlSessionFactory, Class<T> mappedType) {
        return TableInfoCache.getUpdateSqlBuilderCache(mappedType, sqlSessionFactory);
    }

    protected <T> AbstractSqlBuilder<T> getEmptySqlBuilder(JdbcSqlSessionFactory sqlSessionFactory, Class<T> mappedType) {
        return TableInfoCache.getEmptySqlBuilder(mappedType, sqlSessionFactory);
    }

    /**
     * 分页数据整合
     * @param target 用于查询的类，例如条件构造器中的实体类
     * @param mappedType 返回结果的映射类，例如使用T类条件构造器查询的结果映射给M类，正常情况下，T与M是同一种类型，除非要将结果映射给另一个M类
     */
    protected <M> void buildPageResult(JdbcSqlSessionFactory sqlSessionFactory,
                                       Class<?> target,
                                       Class<?> mappedType,
                                       String selectSql,
                                       DbPageRows<M> dbPageRows,
                                       Object[] params) throws Exception {

        List<M> dataList = new ArrayList<>();

        CustomSqlSession countSqlSession = this.createCountSqlSession(sqlSessionFactory, target, selectSql, params);
        long count = Converts.toLong(sqlSessionFactory.getJdbcExecutor().selectObj(countSqlSession));

        if (count > 0) {
            CustomSqlSession selectSqlSession = this.createPageSqlSession(sqlSessionFactory, target, mappedType, selectSql,
                    dbPageRows.getPageIndex(), dbPageRows.getPageSize(), params);
            dataList = sqlSessionFactory.getJdbcExecutor().selectList(selectSqlSession);
        }
        dbPageRows.setTotal(count).setData(dataList);
    }

    protected <T, M> CustomSqlSession createCountSqlSession(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target, String selectSql, Object[] params) throws Exception {
        // 格式化并获取selectCountSQL
        String selectCountSql = SqlExecTemplate.format(SqlExecTemplate.SELECT_COUNT, selectSql);
        BaseSqlExecutorBody executorBody = ExecuteBodyHelper.createSelectIf(
                Long.class,
                selectCountSql,
                sqlPrintSupport,
                params
        );
        AbstractSqlBuilder<T> sqlBuilder = this.getSelectSqlBuilder(sqlSessionFactory, target);
        executorBody.setFieldTypeHandlerMap(sqlBuilder.getFieldTypeHandlerMap());
        return sqlSessionFactory.createSqlSession(executorBody);
    }

    protected <T, M> CustomSqlSession createPageSqlSession(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target, Class<M> mappedType,
                                                           String selectSql, int pageIndex, int pageSize, Object[] params) throws Exception {
        DatabaseAdapter databaseAdapter = sqlSessionFactory.getDatabaseAdapter();
        AbstractSqlBuilder<T> sqlBuilder = getSelectSqlBuilder(sqlSessionFactory, target);
        selectSql = databaseAdapter.handlePage(selectSql, pageIndex, pageSize);
        BaseSqlExecutorBody executorBody = ExecuteBodyHelper.createSelectIf(
                mappedType,
                selectSql,
                sqlPrintSupport,
                params
        );
        executorBody.setFieldTypeHandlerMap(sqlBuilder.getFieldTypeHandlerMap());
        return sqlSessionFactory.createSqlSession(executorBody);
    }

    protected <T> String joinWrapperTargetSql(JdbcSqlSessionFactory sqlSessionFactory, JoinConditionWrapper<T> wrapper, Class<T> target) throws Exception {
        HandleSelectSqlBuilder<T> sqlBuilder = (HandleSelectSqlBuilder<T>) getSelectSqlBuilder(sqlSessionFactory, target);
        return sqlBuilder.executeSqlBuilder(wrapper);
    }

    /**
     * 创建同步属性值的查询结果注入处理器
     */
    protected <T> SyncResultHandler<T> createSyncResultHandler(JdbcSqlSessionFactory sqlSessionFactory, Class<T> target,
                                                               Object[] params) {
        SyncQueryWrapper<T> queryWrapper = (SyncQueryWrapper<T>) params[0];
        return SyncResultHandler.create(sqlSessionFactory, target, queryWrapper.getSyncPropertyList());
    }
}
